package com.github.cakin.shiro.chapter20.codec;

import org.apache.commons.codec.binary.Hex;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

/**
 * @className: HmacSHA256Utils
 * @description: 使用指定的密钥对内容生成消息摘要（散列值）
 * @date: 2020/5/29
 * @author: cakin
 */
public class HmacSHA256Utils {
    /**
     * 功能描述：使用指定的密钥对内容（字符串）生成消息摘要（散列值）
     *
     * @param key     密钥
     * @param content 内容
     * @return String 消息摘要
     * @author cakin
     * @date 2020/5/29
     */
    public static String digest( String key, String content ) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            // 密钥转字节数组
            byte[] secretByte = key.getBytes("utf-8");
            // 内容转字节数组
            byte[] dataBytes = content.getBytes("utf-8");
            // 初始化密钥，指定该密钥用于HMACSHA256
            SecretKey secret = new SecretKeySpec(secretByte, "HMACSHA256");
            // 传入密钥进行初始化
            mac.init(secret);
            // 生成消息摘要
            byte[] doFinal = mac.doFinal(dataBytes);
            byte[] hexB = new Hex().encode(doFinal);
            return new String(hexB, "utf-8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 功能描述：使用指定的密钥对内容（map）生成消息摘要（散列值）
     *
     * @param key 密钥
     * @param map 内容
     * @return String 消息摘要
     * @author cakin
     * @date 2020/5/29
     */
    public static String digest( String key, Map<String, ?> map ) {
        StringBuilder s = new StringBuilder();
        // map存储是无序的，所以这里先将map中的值存到TreeSet中
        TreeSet<String> treeSet = new TreeSet<String>();

        for (Object values : map.values()) {
            // 针对值是String数组的情况
            if (values instanceof String[]) {
                for (String value : (String[]) values) {
                    treeSet.add(value);
                }
            } else if (values instanceof List) { // 针对值是List的情况
                for (String value : (List<String>) values) {
                    treeSet.add(value);
                }
            } else { // 针对值是其他情况
                treeSet.add((String) values);
            }
        }
        for (Object str : treeSet) {
            s.append((String) str);
        }
        // 调用重载方法
        return digest(key, s.toString());
    }
}
